import numpy as np
import os,sys
import PySide2
import math

dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path

from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QCoreApplication
import matplotlib
matplotlib.use("Qt5Agg") # 声明使用pyqt5
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg # pyqt5的画布
import matplotlib.pyplot as plt
# matplotlib.figure 模块提供了顶层的Artist(图中的所有可见元素都是Artist的子类)，它包含了所有的plot元素
from matplotlib.figure import Figure
ui_file = os.path.join(os.path.dirname(__file__), "main.ui")
def get_max_value(martix):
      '''
      得到矩阵中每一列最大的值
      '''
      res_array=[]
      for j in range(len(martix[0])):
        one_array=[]
        for i in range(len(martix)):
          one_array.append(int(martix[i][j]))
        res_array.append(max(one_array))
      return res_array
def get_min_value(martix):
  '''
  得到矩阵中每一列最小的值
  '''
  res_array=[]
  for j in range(len(martix[0])):
    one_array=[]
    for i in range(len(martix)):
      one_array.append(int(martix[i][j]))
    res_array.append(min(one_array))
  return res_array

def quadratic_fitting(X,Y):
    X = np.array(X)
    Y = np.array(Y)
    S0,S1,S2,S3,S4 = len(X),sum(X),sum(X*X),sum(X**3),sum(X**4)
    V0,V1,V2 = sum(Y), sum(Y*X), sum(Y*X*X)
    coeff_mat = np.array([[S0,S1,S2],[S1,S2,S3],[S2,S3,S4]])
    target_vec = np.array([V0,V1,V2])
    inv_coeff_mat = np.linalg.inv(coeff_mat)
    fitted_coeff = np.matmul(inv_coeff_mat,target_vec)
    resulted_Ys = fitted_coeff[0]+fitted_coeff[1]*X+fitted_coeff[2]*X*X
    return resulted_Ys

class MyMatplotlibFigure(FigureCanvasQTAgg):
  """
  创建一个画布类，并把画布放到FigureCanvasQTAgg
  """
  def __init__(self, width=20, heigh=20, dpi=300):
    plt.rcParams['figure.facecolor'] = 'w' # 设置窗体颜色
    plt.rcParams['axes.facecolor'] = 'b' # 设置绘图区颜色
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置中文字体为黑体
    # 创建一个Figure,该Figure为matplotlib下的Figure，不是matplotlib.pyplot下面的Figure
    self.figs = Figure(figsize=(width, heigh), dpi=dpi)
    super(MyMatplotlibFigure, self).__init__(self.figs) # 在父类种激活self.fig，
    self.axes = self.figs.add_subplot(111) # 添加绘图区
  def mat_plot_drow_axes(self, t, s):
    """
    用清除画布刷新的方法绘图
    :return:
    """
    self.axes.cla() # 清除绘图区

    self.axes.spines['top'].set_visible(False) # 顶边界不可见
    self.axes.spines['right'].set_visible(False) # 右边界不可见
    self.axes.margins(0.02)
    # 设置左、下边界在（0，0）处相交
    self.axes.spines['bottom'].set_position(('data', 0)) # 设置y轴线原点数据为 0
    self.axes.spines['left'].set_position(('data', 0)) # 设置x轴线原点数据为 0
    self.axes.plot(t, s, 'o-r', linewidth=0.2)
    self.figs.canvas.draw() # 这里注意是画布重绘，self.figs.canvas
    self.figs.canvas.flush_events() # 画布刷新self.figs.canvas

error_messgae="不支持的期权类型，请重新选择"

class Stats:
    def __init__(self):
        # 从文件中加载UI定义

        # 从 UI 定义中动态 创建一个相应的窗口对象
        # 注意：里面的控件对象也成为窗口对象的属性了
        # 比如 self.ui.button , self.ui.textEdit
        super().__init__()
        self.ui = QUiLoader().load(ui_file)
        loader = QUiLoader()
        # 初始化 gv_visual_data 的显示
        self.canvas = MyMatplotlibFigure(width=20, heigh=20, dpi=300) # 实例化一个FigureCanvasMyMatplotlibFigure(width=5, heigh=4, dpi=100)
        self.ui.ComputeButton.clicked.connect(self.handleCalc)
        self.ui.QuitButton.clicked.connect(QCoreApplication.quit)




    def handleCalc(self):
        np.random.seed(20000)
        K = float(self.ui.K.toPlainText())
        I = int(self.ui.I.toPlainText())
        S0 = float(self.ui.S0.toPlainText())
        T = float(self.ui.T.toPlainText())
        r = float(self.ui.r.toPlainText())
        sigma = float(self.ui.sigma.toPlainText())
        m = int(self.ui.m.toPlainText())


        dt =T/m

        S=np.zeros((m+1,I))
        S[0]=S0
        for t in range(1,m+1):
            z=np.random.standard_normal(I)
            S[t]=S[t-1]*np.exp((r-0.5*sigma**2)*dt+ sigma *math.sqrt(dt)*z)
        Smax =get_max_value(S)
        Smin = get_min_value(S)
        if self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "买权":
            option_prices=np.exp(-r*T)*np.maximum(S[-1,:]-K,0)

        elif self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.exp(-r*T)*np.maximum(K-S[-1,:],0)

        elif self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "固定回望" and self.ui.put_call.currentText() == "买权":
            option_prices=np.exp(-r*T)*np.maximum(np.array(Smax)-K,0)

        elif self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "固定回望" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.exp(-r*T)*np.maximum(K-np.array(Smin),0)

        elif self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "买权":
            option_prices=np.exp(-r*T)*np.maximum(S[-1,:]-np.array(Smin),0)

        elif self.ui.category.currentText() == "欧式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.exp(-r*T)*np.maximum(np.array(Smax)-S[-1,:],0)

        elif self.ui.category.currentText() == "亚式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "买权":
            option_prices=np.exp(-r*T)*np.maximum(np.mean(S,axis=0)-K,0)

        elif self.ui.category.currentText() == "亚式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.exp(-r*T)*np.maximum(K-np.mean(S,axis=0),0)

        elif self.ui.category.currentText() == "亚式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "买权":
            option_prices=np.exp(-r*T)*np.maximum(np.mean(S,axis=0)-np.array(Smin),0)

        elif self.ui.category.currentText() == "亚式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.exp(-r*T)*np.maximum(np.array(Smax)-np.mean(S,axis=0),0)

        elif self.ui.category.currentText() == "亚式" and self.ui.lookback.currentText() == "固定回望":
            QMessageBox.about(self.ui,
                          '错误提示',error_messgae)

        elif self.ui.category.currentText() == "美式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "买权":
            option_prices=np.maximum(S[-1,:]-K,0)
            for i in range(m-1,0,-1):
                # 期权价格贴现到当前时刻。
                option_prices *= np.exp(-r*dt)
                # 选出实值期权
                in_index, in_option_prices = np.where(option_prices>0)[0],option_prices[option_prices>0]
                # 回归拟合更新期权价格。
                in_option_prices = quadratic_fitting(S[i,in_index], in_option_prices)
                option_prices=np.zeros(I)
                option_prices[in_index]=in_option_prices
                # 判断是不是应该执行期权，并更新价格。
                option_prices = np.maximum(option_prices,S[i,:]-K)
            # 递推回的是初始时刻下一时刻，所以还需要再贴现一次。
            option_prices *= np.exp(-r*dt)


        elif self.ui.category.currentText() == "美式" and self.ui.lookback.currentText() == "常规" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.maximum(K-S[-1,:],0)
            for i in range(m-1,0,-1):
                # 期权价格贴现到当前时刻。
                option_prices *= np.exp(-r*dt)
                # 选出实值期权
                in_index, in_option_prices = np.where(option_prices>0)[0],option_prices[option_prices>0]
                # 回归拟合更新期权价格。
                in_option_prices = quadratic_fitting(S[i,in_index], in_option_prices)
                option_prices=np.zeros(I)
                option_prices[in_index]=in_option_prices
                # 判断是不是应该执行期权，并更新价格。
                option_prices = np.maximum(option_prices,K-S[i,:])
            # 递推回的是初始时刻下一时刻，所以还需要再贴现一次。
            option_prices *= np.exp(-r*dt)

        elif self.ui.category.currentText() == "美式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "买权":
            option_prices=np.maximum(S[-1,:]-np.array(Smin),0)
            for i in range(m-1,0,-1):
                # 期权价格贴现到当前时刻。
                option_prices *= np.exp(-r*dt)
                # 选出实值期权
                in_index, in_option_prices = np.where(option_prices>0)[0],option_prices[option_prices>0]
                # 回归拟合更新期权价格。
                in_option_prices = quadratic_fitting(S[i,in_index], in_option_prices)
                option_prices=np.zeros(I)
                option_prices[in_index]=in_option_prices
                # 判断是不是应该执行期权，并更新价格。
                option_prices = np.maximum(option_prices,S[i,:]-np.array(Smin))
            # 递推回的是初始时刻下一时刻，所以还需要再贴现一次。
            option_prices *= np.exp(-r*dt)


        elif self.ui.category.currentText() == "美式" and self.ui.lookback.currentText() == "浮动回望" and self.ui.put_call.currentText() == "卖权":
            option_prices=np.maximum(np.array(Smax)-S[-1,:],0)
            for i in range(m-1,0,-1):
                # 期权价格贴现到当前时刻。
                option_prices *= np.exp(-r*dt)
                # 选出实值期权
                in_index, in_option_prices = np.where(option_prices>0)[0],option_prices[option_prices>0]
                # 回归拟合更新期权价格。
                in_option_prices = quadratic_fitting(S[i,in_index], in_option_prices)
                option_prices=np.zeros(I)
                option_prices[in_index]=in_option_prices
                # 判断是不是应该执行期权，并更新价格。
                option_prices = np.maximum(option_prices,np.array(Smax)-S[i,:])
            # 递推回的是初始时刻下一时刻，所以还需要再贴现一次。
            option_prices *= np.exp(-r*dt)

        elif self.ui.category.currentText() == "美式" and self.ui.lookback.currentText() == "固定回望":
            QMessageBox.about(self.ui,
                          '错误提示',error_messgae)
        try:
            option_value=np.mean(option_prices)

            # QtGui.QApplication.instance().exec_()

            I0 = np.array(range(I))+1

            QMessageBox.about(self.ui,
                              '期权价格为：',
                              str(option_value))

            title=self.ui.category.currentText()+self.ui.put_call.currentText()+"("+self.ui.lookback.currentText()+")"
            self.canvas.mat_plot_drow_axes(I0, option_prices)
            self.canvas.figs.suptitle(title) # 设置标题
            self.canvas.show() # 调用show方法呈现图形
            self.canvas.draw()
        except:
            pass










app = QApplication(sys.argv)
stats = Stats()
stats.ui.show()
app.exec_()
